Add support for examples
authorBen Longbons <b.r.longbons@gmail.com>
Thu, 10 Jul 2014 22:13:53 +0000 (15:13 -0700)
committerBen Longbons <b.r.longbons@gmail.com>
Fri, 11 Jul 2014 20:41:26 +0000 (13:41 -0700)
src/cargo/core/manifest.rs
src/cargo/util/toml.rs
tests/test_cargo_compile.rs

index 981547116941c8127d9c2e18c4ed712166ca11f0..6d130aa6f59d8b4ae980fd5abd0f5817cccb71dd 100644 (file)
@@ -343,6 +343,16 @@ impl Target {
         }
     }
 
+    pub fn example_target(name: &str, src_path: &Path, profile: &Profile) -> Target {
+        Target {
+            kind: BinTarget,
+            name: name.to_string(),
+            src_path: src_path.clone(),
+            profile: profile.clone(),
+            metadata: None
+        }
+    }
+
     pub fn get_name<'a>(&'a self) -> &'a str {
         self.name.as_slice()
     }
index 232cf0a0e5dbb7b13dfe65fc3ee45e5fd07b4f33..b8c62cc07a37b9071faa0e54fc13c65c61d5195a 100644 (file)
@@ -14,7 +14,8 @@ use util::{CargoResult, Require, human};
 #[deriving(Clone)]
 pub struct Layout {
     lib: Option<Path>,
-    bins: Vec<Path>
+    bins: Vec<Path>,
+    examples: Vec<Path>,
 }
 
 impl Layout {
@@ -31,6 +32,7 @@ impl Layout {
 pub fn project_layout(root: &Path) -> Layout {
     let mut lib = None;
     let mut bins = vec!();
+    let mut examples = vec!();
 
     if root.join("src/lib.rs").exists() {
         lib = Some(root.join("src/lib.rs"));
@@ -46,9 +48,16 @@ pub fn project_layout(root: &Path) -> Layout {
         .map(|mut i| i.collect())
         .map(|found| bins.push_all_move(found));
 
+    let _ = fs::readdir(&root.join("examples"))
+        .map(|v| v.move_iter())
+        .map(|i| i.filter(|f| f.extension_str() == Some("rs")))
+        .map(|mut i| i.collect())
+        .map(|found| examples.push_all_move(found));
+
     Layout {
         lib: lib,
-        bins: bins
+        bins: bins,
+        examples: examples,
     }
 }
 
@@ -129,6 +138,7 @@ pub fn parse(toml: &str, file: &str) -> CargoResult<toml::Table> {
 
 type TomlLibTarget = TomlTarget;
 type TomlBinTarget = TomlTarget;
+type TomlExampleTarget = TomlTarget;
 
 /*
  * TODO: Make all struct fields private
@@ -157,6 +167,7 @@ pub struct TomlManifest {
     project: Option<Box<TomlProject>>,
     lib: Option<Vec<TomlLibTarget>>,
     bin: Option<Vec<TomlBinTarget>>,
+    example: Option<Vec<TomlExampleTarget>>,
     dependencies: Option<HashMap<String, TomlDependency>>,
     dev_dependencies: Option<HashMap<String, TomlDependency>>
 }
@@ -227,6 +238,22 @@ fn inferred_bin_targets(name: &str, layout: &Layout) -> Option<Vec<TomlTarget>>
     }).collect())
 }
 
+fn inferred_example_targets(layout: &Layout) -> Option<Vec<TomlTarget>> {
+    Some(layout.examples.iter().filter_map(|ex| {
+        let name = ex.filestem_str().map(|f| f.to_string());
+
+        name.map(|name| {
+            TomlTarget {
+                name: name,
+                crate_type: None,
+                path: Some(ex.display().to_string()),
+                test: None,
+                plugin: None,
+            }
+        })
+    }).collect())
+}
+
 impl TomlManifest {
     pub fn to_manifest(&self, source_id: &SourceId, layout: &Layout)
         -> CargoResult<(Manifest, Vec<Path>)>
@@ -284,9 +311,18 @@ impl TomlManifest {
             }).collect())
         };
 
+        let examples = if self.example.is_none() || self.example.get_ref().is_empty() {
+            inferred_example_targets(layout)
+        } else {
+            Some(self.example.get_ref().iter().map(|t| {
+                t.clone()
+            }).collect())
+        };
+
         // Get targets
         let targets = normalize(lib.as_ref().map(|l| l.as_slice()),
                                 bins.as_ref().map(|b| b.as_slice()),
+                                examples.as_ref().map(|e| e.as_slice()),
                                 &metadata);
 
         if targets.is_empty() {
@@ -386,10 +422,11 @@ struct TomlTarget {
 
 fn normalize(lib: Option<&[TomlLibTarget]>,
              bin: Option<&[TomlBinTarget]>,
+             example: Option<&[TomlExampleTarget]>,
              metadata: &Metadata)
              -> Vec<Target>
 {
-    log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin);
+    log!(4, "normalizing toml targets; lib={}; bin={}; example={}", lib, bin, example);
 
     enum TestDep { Needed, NotNeeded }
 
@@ -444,6 +481,20 @@ fn normalize(lib: Option<&[TomlLibTarget]>,
         }
     }
 
+    fn example_targets(dst: &mut Vec<Target>, examples: &[TomlExampleTarget],
+                   default: |&TomlExampleTarget| -> String) {
+        for ex in examples.iter() {
+            let path = ex.path.clone().unwrap_or_else(|| default(ex));
+
+            let profile = &Profile::default_test().test(false);
+            {
+                dst.push(Target::example_target(ex.name.as_slice(),
+                                            &Path::new(path.as_slice()),
+                                            profile));
+            }
+        }
+    }
+
     let mut ret = Vec::new();
 
     match (lib, bin) {
@@ -462,5 +513,14 @@ fn normalize(lib: Option<&[TomlLibTarget]>,
         (None, None) => ()
     }
 
+
+    match example {
+        Some(ref examples) => {
+            example_targets(&mut ret, examples.as_slice(),
+                        |ex| format!("examples/{}.rs", ex.name));
+        },
+        None => ()
+    }
+
     ret
 }
index 7c2d0affb11ef86a92633bb5f46ddc6b01c9e60f..489c77753f5b46ee9194f24bf85357fa29548b58 100644 (file)
@@ -1072,3 +1072,69 @@ test!(verbose_release_build_deps {
                     hash1 = hash1,
                     hash2 = hash2).as_slice());
 })
+
+test!(explicit_examples {
+    let mut p = project("world");
+    p = p.file("Cargo.toml", r#"
+            [package]
+            name = "world"
+            version = "1.0.0"
+            authors = []
+
+            [[lib]]
+            name = "world"
+            path = "src/lib.rs"
+
+            [[example]]
+            name = "hello"
+            path = "examples/ex-hello.rs"
+
+            [[example]]
+            name = "goodbye"
+            path = "examples/ex-goodbye.rs"
+        "#)
+        .file("src/lib.rs", r#"
+            pub fn get_hello() -> &'static str { "Hello" }
+            pub fn get_goodbye() -> &'static str { "Goodbye" }
+            pub fn get_world() -> &'static str { "World" }
+        "#)
+        .file("examples/ex-hello.rs", r#"
+            extern crate world;
+            fn main() { println!("{}, {}!", world::get_hello(), world::get_world()); }
+        "#)
+        .file("examples/ex-goodbye.rs", r#"
+            extern crate world;
+            fn main() { println!("{}, {}!", world::get_goodbye(), world::get_world()); }
+        "#);
+
+    assert_that(p.cargo_process("cargo-test"), execs());
+    assert_that(process(p.bin("test/hello")), execs().with_stdout("Hello, World!\n"));
+    assert_that(process(p.bin("test/goodbye")), execs().with_stdout("Goodbye, World!\n"));
+})
+
+test!(implicit_examples {
+    let mut p = project("world");
+    p = p.file("Cargo.toml", r#"
+            [package]
+            name = "world"
+            version = "1.0.0"
+            authors = []
+        "#)
+        .file("src/lib.rs", r#"
+            pub fn get_hello() -> &'static str { "Hello" }
+            pub fn get_goodbye() -> &'static str { "Goodbye" }
+            pub fn get_world() -> &'static str { "World" }
+        "#)
+        .file("examples/hello.rs", r#"
+            extern crate world;
+            fn main() { println!("{}, {}!", world::get_hello(), world::get_world()); }
+        "#)
+        .file("examples/goodbye.rs", r#"
+            extern crate world;
+            fn main() { println!("{}, {}!", world::get_goodbye(), world::get_world()); }
+        "#);
+
+    assert_that(p.cargo_process("cargo-test"), execs());
+    assert_that(process(p.bin("test/hello")), execs().with_stdout("Hello, World!\n"));
+    assert_that(process(p.bin("test/goodbye")), execs().with_stdout("Goodbye, World!\n"));
+})